畢竟 monad 是來自於數學上的概念,所以就算某個type 實作了 Monad
的 instance ,嚴格定義上我們也不能說它是 monad 。
就像 monoid law 或 functor law
第一個規則是如果我們用 return
一個值 a
並把它用 >>=
apply 到某個 function f
也就是 return a >>= f
,結果應該等於 f a
return 1 >>= (\x -> Just (x+1)) -- Just 2
(\x -> Just (x+1)) 1 -- Just 2
return 'a' >>= (\x -> [x,x,x]) --- "aaa"
(\x -> [x,x,x]) 'a' --- "aaa"
return
的 type 的是 Monad m => a -> m a
,那 return 1
就變成一個是 monadic value 然後 >>=
的 type 是 Monad m => m a -> (a -> m b) -> m b
,所以 return 1
變成 m a
然後 (\x -> Just (x+1))
就是 (a → m b)
所以最後將 return 1
丟進去 (\x -> Just (x+1))
就會是 Just 2
。
那反過來說就相當簡單 (\x -> Just (x+1)) 1
,就是很簡單的 (a → m b)
傳入一個 a
最後回傳 m b
。
簡單來說就是 >>=
將左邊的運算元的 monadic value 的 context 打開後餵進去右邊的 function 。那本來右邊的 function 就是將一個 value 變成 monadic value 的 function 。所以我直接將這個「 function apply 給那個 value 」跟「將同樣的值但是是包裝在 context 用 >>=
丟進同樣的function 」的結果會是一樣的。
第二個規則就是如果我們將 monadic value a
用 >>=
apply 給 return
那結果應該是 a
Just 1 >>= (\x -> return x) -- Just 1
"aaa" >>= (\x -> return x) -- "aaa"
如上面所提到的 >>=
簡單來說就是將 monadic value 把它從 context 中的值給 apply 到一個function 但最後維持一樣的 context 。return
則是將一個 value 轉換成 monadic value。
那所以我將一個 monadic value 的值從 context 中取出來並再包裝一次 context 當然會是一樣的 monadic value 。
第三個規則是如果我們用 >>=
串接不同的 function 不管誰先執行結果都應該一樣。
(Just 2 >>= (\x -> Just(x*2))) >>= (\x -> Just(x*3)) -- Just 12
Just 2 >>= (\x -> Just(x*2) >>= (\x -> Just(x*3))) -- Just 12
(Just 2 >>= (\x -> Just(x+3))) >>= (\x -> Just(x*3)) -- Just 15
Just 2 >>= (\x -> Just(x+3) >>= (\x -> Just(x*3))) -- Just 15
這邊看的出來不管我們是先執行 (Just 2 >>= (\x -> Just(x*2)))
然後再 >>= (\x -> Just(x*3)
或者是先讓 (\x -> Just(x*2) >>= (\x -> Just(x*3)))
後再用 >>=
將 Just 2
傳進去,結果都會是一樣的。
跟一般的 function composition .
一樣,對於 monadic function 也是有 composition 的語法 <=<
:t (<=<) -- (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
((\x -> Just(x*3)) <=< (\x -> Just(x+3))) 2 -- Just 12
而 <=<
的定義也跟 .
很像,從 f . g = (\x -> f (g x))
變成了 f <=< g = (\x -> g x >>= f)
。